package org.wh1tew0lf.vframe.framework;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;

import org.json.JSONObject;
import org.wh1tew0lf.vframe.framework.vframeexception.VFrameException;
import org.wh1tew0lf.vframe.framework.vframeexception.VIllegalArgumentOfMethodException;
import org.wh1tew0lf.vframe.framework.vframeexception.VInvocationTargetException;
import org.wh1tew0lf.vframe.framework.vframeexception.VMethodNameIsNullException;
import org.wh1tew0lf.vframe.framework.vframeexception.VModuleMethodNotAccessibleException;
import org.wh1tew0lf.vframe.framework.vframeexception.VModuleMethodNotFoundException;
import org.wh1tew0lf.vframe.framework.vframeexception.VModuleClassIsNullException;
import org.wh1tew0lf.vframe.framework.vframeexception.VModuleKeyIsNullException;
import org.wh1tew0lf.vframe.framework.vframeexception.VModuleManagerNotInitializedException;
import org.wh1tew0lf.vframe.framework.vframeexception.VModuleNameIsNullException;
import org.wh1tew0lf.vframe.framework.vframeexception.VModuleNotFoundException;
import org.wh1tew0lf.vframe.framework.vframeexception.VUnknownReturnedTypeOfMethod;
import org.wh1tew0lf.vframe.model.datamodel.IReadonlyDataModel;
import org.wh1tew0lf.vframe.model.logicmodel.ILogicModel;
import org.wh1tew0lf.vframe.module.AbstractModule;

public final class VModuleManager {

	private static ConcurrentHashMap<String, Class<? extends AbstractModule>> moduleClassMap;
	private static ConcurrentHashMap<String, String> moduleKeyMap;

	static {
		moduleClassMap = new ConcurrentHashMap<String, Class<? extends AbstractModule>>();
		moduleKeyMap = new ConcurrentHashMap<String, String>();
	}

	public static boolean updateModuleClass(String moduleName,
			Class<? extends AbstractModule> moduleClass, String oldKey,
			String newKey) throws VFrameException {
		// TODO Log CALL TRACE
		if (moduleName == null) {
			throw new VModuleNameIsNullException();
		}
		if (moduleClass == null) {
			throw new VModuleClassIsNullException();
		}
		if (moduleClassMap == null || moduleKeyMap == null) {
			throw new VModuleManagerNotInitializedException();
		}
		if (newKey == null) {
			throw new VModuleKeyIsNullException();
		}
		String storedKey = moduleKeyMap.get(moduleName);
		if (storedKey != null && (!storedKey.equals(oldKey))) {
			return false;
		}
		moduleClassMap.put(moduleName, moduleClass);
		moduleKeyMap.put(moduleName, newKey);
		storedKey = null;
		return true;
	}

	public static boolean removeModule(String moduleName, String key)
			throws VFrameException {
		if (moduleName == null) {
			throw new VModuleNameIsNullException();
		}
		if (key == null) {
			throw new VModuleKeyIsNullException();
		}
		String storedKey = moduleKeyMap.get(moduleName);
		if (storedKey == null) {
			throw new VModuleNotFoundException(moduleName);
		}
		if (storedKey.equals(key)) {
			moduleKeyMap.remove(moduleName);
			moduleClassMap.remove(moduleName);
			return true;
		} else {
			return false;
		}
	}

	@SuppressWarnings("unchecked")
	protected static ArrayList<IReadonlyDataModel> doMethod(
			ArrayList<ILogicModel> logicModelList, String moduleName,
			String methodName, JSONObject extraInfo) throws VFrameException {
		if (moduleName == null) {
			throw new VModuleNameIsNullException();
		}
		if (methodName == null) {
			throw new VMethodNameIsNullException();
		}
		Class<? extends AbstractModule> moduleClass = moduleClassMap
				.get(moduleName);
		if (moduleClass == null) {
			throw new VModuleNotFoundException(moduleName);
		}
		Method method = null;
		try {
			method = moduleClass.getDeclaredMethod(methodName, new Class[] {
					ArrayList.class, JSONObject.class });
		} catch (SecurityException e) {
			throw new VModuleMethodNotAccessibleException(moduleName,
					methodName);
		} catch (NoSuchMethodException e) {
			throw new VModuleMethodNotFoundException(moduleName, methodName);
		}
		if (method == null) {
			throw new VModuleMethodNotFoundException(moduleName, methodName);
		}
		method.setAccessible(true);
		Object tempResult = null;
		ArrayList<IReadonlyDataModel> result = null;
		try {
			tempResult = method.invoke(moduleClass, new Object[] {
					logicModelList, extraInfo });
			if (tempResult instanceof ArrayList) {
				result = (ArrayList<IReadonlyDataModel>) tempResult;
			} else {
				throw new VUnknownReturnedTypeOfMethod(moduleName, methodName);
			}
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
			throw new VIllegalArgumentOfMethodException(moduleName, methodName);
		} catch (IllegalAccessException e) {
			e.printStackTrace();
			throw new VModuleMethodNotAccessibleException(moduleName,
					methodName);
		} catch (InvocationTargetException e) {
			e.printStackTrace();
			throw new VInvocationTargetException(moduleName, methodName);
		}
		tempResult = null;
		method = null;
		return result;
	}

	protected static boolean isAuthorizedModule(String moduleName,
			String moduleKey) throws VFrameException {
		if (moduleName == null) {
			throw new VModuleNameIsNullException();
		}
		if (moduleKey == null) {
			throw new VModuleKeyIsNullException();
		}
		String storedKey = moduleKeyMap.get(moduleName);
		if (storedKey != null && storedKey.equals(moduleKey)) {
			return true;
		} else {
			return false;
		}
	}

}
